package identify

// #cgo pkg-config: yoyoai
// #include "yoyoai_helper.h"
import "C"
import (
	"fmt"
	"reflect"
	"sync"
	"time"
	"unsafe"

	"golang.org/x/exp/slices"
)

// Control 识别模型控制器
type Control struct {
	mutex  sync.Mutex    // 操作锁
	handle C.YYRegHandle // 识别模型句柄
}

// NewControl 创建控制器
func NewControl() *Control {
	return &Control{}
}

// 置空全部句柄变量
func (p *Control) reset() {
	p.handle = nil
}

// GetVersion 获取识别模型的版本信息
//
//	@return	模型外部版本信息
//	@return	模型内部版本信息
//	@return	异常错误
func (p *Control) GetVersion() (string, string, error) {
	// 加锁
	p.mutex.Lock()
	defer p.mutex.Unlock()

	// 调用链接库方法
	version := C.recogVersion()
	if version == nil {
		return "", "", ErrResNullPtr
	}

	// 拼接版本信息
	versionStr := fmt.Sprintf(
		"%d.%d.%d.%d",
		int(version.Major),
		int(version.Minor),
		int(version.Revision),
		int(version.Build),
	)
	// 是否包含后缀
	ext := C.GoString(version.Ext)
	if len(ext) > 0 {
		versionStr += "-" + ext
	}
	// 提取内部版本号
	var internalVersion string
	if version.Sub != nil {
		internalVersion = C.GoString(version.Sub)
	}

	// OK
	return versionStr, internalVersion, nil
}

// Init 初始化识别模型实例句柄
//
//	@param	params	初始化参数
//	@return	错误信息
func (p *Control) Init(params InitParams) error {
	// 加锁保护
	p.mutex.Lock()
	defer p.mutex.Unlock()

	// 是否需要初始化
	if p.handle != nil {
		return nil
	}

	// 构建初始化参数
	input := &C.RecogInitParam{
		vadCode:  C.CString(params.AiKey),
		workPath: C.CString(params.WorkPath),
		featMode: C.RecogFeatureModeType(params.FeatMode),
		platform: C.RecogPlatformType(params.UseProcessor),
	}
	// 延迟释放参数
	defer func() {
		C.free(unsafe.Pointer(input.vadCode))
		C.free(unsafe.Pointer(input.workPath))
	}()

	// 执行初始化
	p.handle = C.recogEngineInit(input)
	if p.handle == nil {
		return ErrInitLib
	}

	// OK
	return nil
}

// UnInit 释放识别模型实例句柄
//
//	@return	错误信息
func (p *Control) UnInit() error {
	// 加锁保护
	p.mutex.Lock()
	defer p.mutex.Unlock()

	// 检查句柄是否为空
	if p.handle == nil {
		return nil
	}

	// 执行释放
	res := C.recogEngineUninit(p.handle)
	// 转换状态码
	err := convertStatusCode(int(res))
	if err != nil {
		return err
	}

	// 重置
	p.reset()

	// OK
	return nil
}

// Process 识别方法
//
//	@param	img	图像资源
//	@return	识别结果
//	@return	错误信息
func (p *Control) Process(img *ImageInfo) (*ProcessOutput, error) {
	// 加锁保护
	p.mutex.Lock()
	defer p.mutex.Unlock()

	// 检查句柄是否为空
	if p.handle == nil {
		return nil, ErrNotInitLib
	}

	// 定义识别开始时间
	startTime := time.Now()

	// 赋值图像信息
	imgMat, imgMatFree := img.CRecogMat()
	defer imgMatFree()
	// 定义响应结构
	var output C.RecogOutput
	// 执行识别
	res := C.recogProcess(p.handle, imgMat, &output)
	// 转换状态码
	err := convertStatusCode(int(res))
	if err != nil {
		return nil, err
	}

	// ----------------------------------- 转换为Go结构(深拷贝) ----------------------------------- //
	// 判断识别ID
	if output.recogID == 0 {
		return nil, ErrResRecordIDEmpty
	}
	// 判断是否有识别结果
	if output.ycAttriSize == 0 || output.vecYcAttri == nil || output.featSize == 0 || output.vecFeat == nil {
		return nil, ErrResEmpty
	}

	// 定义响应结果
	result := &ProcessOutput{
		MagicCode: int32(output.magicCode),
		ID:        RecordID(output.recogID),
		Duration:  time.Since(startTime).Milliseconds(),
	}

	// 提取YC信息列表
	for index := 0; index < int(output.ycAttriSize); index++ {
		// 获取YC信息列表的元素引用
		element := C.getRecogYcAttriListItem(output.vecYcAttri, C.size_t(index))
		// 赋值YC信息
		ycInfo := &YcAttri{
			Score:    float32(element.score),
			RefScore: float32(element.refScore),
			Code:     C.GoString(element.ycCode),
		}
		// 追加到YC列表
		result.YcList = append(result.YcList, ycInfo)
	}

	// 提取特征类目列表
	for index := 0; index < int(output.featSize); index++ {
		// 获取特征信息列表的元素引用
		element := C.getRecogFeatListItem(output.vecFeat, C.size_t(index))
		// 提取特征信息
		var fetaData []float32
		fetaDataHeader := (*reflect.SliceHeader)(unsafe.Pointer(&fetaData))
		fetaDataHeader.Cap = int(element.dataSize)
		fetaDataHeader.Len = int(element.dataSize)
		fetaDataHeader.Data = uintptr(unsafe.Pointer(element.data))
		// 拷贝特征信息
		fetaDataCopy := make([]float32, len(fetaData))
		copy(fetaDataCopy, fetaData)
		// 追加至列表
		result.FeatList = append(result.FeatList, &Feature{
			Mode: FeatureMode(element.featMode),
			Data: fetaDataCopy,
		})
	}

	// 返回识别结果
	return result, nil
}

// GetImageAreaData 获取识别图片
//
//	调用该接口后C管理的识别图片将会被释放
//
//	@param	img	图像信息
//	@return	识别图片资源
//	@return	错误信息
func (p *Control) GetImageAreaData(img *ImageInfo) ([]byte, error) {
	// 加锁保护
	p.mutex.Lock()
	defer p.mutex.Unlock()

	// 赋值入参信息
	var imgMatFree func()
	var imgIn C.RecogImageBlob
	imgIn.imgMode = C.EXTERNAL_INCOMING
	imgIn.img, imgMatFree = img.CRecogMat()
	defer imgMatFree()

	// 定义接收C变量
	var imgOut C.RecogImageByte
	// 执行获取
	res := C.recogGetImage(&imgIn, &imgOut)
	// 转换状态码
	err := convertStatusCode(int(res))
	if err != nil {
		return nil, err
	}
	// 延迟释放识别图片
	defer func() {
		// 释放C缓存MAP
		_ = C.recogFreeImage(&imgIn, &imgOut)
	}()

	// 转换指针为Go类型，会发生深拷贝
	imgArea := C.GoBytes(unsafe.Pointer(imgOut.data), C.int(imgOut.dataSize))
	// 判断是否拿到图
	if len(imgArea) == 0 {
		return nil, ErrCopyImageFail
	}

	// 返回获取到的图片
	return imgArea, nil
}

// 识别模型设置扩展参数
//
// @param	param	扩展参数键值
// @return	错误信息
func (p *Control) setExpandParam(param *C.RecogExpandParam) error {
	// 加锁
	p.mutex.Lock()
	defer p.mutex.Unlock()

	// 指定KEY需要检查handle不为空
	checkHandleKey := []int{
		C.FEAT_NUM,
		C.IS_INHERIT,
	}
	if slices.Contains(checkHandleKey, int(param.key)) && p.handle == nil {
		return ErrNotInitLib
	}

	// 执行配置
	res := C.recogGetParam(p.handle, param)

	// OK，转换状态码
	return convertStatusCode(int(res))
}

// 识别模型获取扩展参数
//
//	@param	param	扩展参数键值
//	@return	错误信息
func (p *Control) getExpandParam(param *C.RecogExpandParam) error {
	// 加锁
	p.mutex.Lock()
	defer p.mutex.Unlock()

	// 指定KEY需要检查handle不为空
	checkHandleKey := []int{
		C.FEAT_NUM,
		C.IS_INHERIT,
	}
	if slices.Contains(checkHandleKey, int(param.key)) && p.handle == nil {
		return ErrNotInitLib
	}

	// 执行获取
	res := C.recogGetParam(p.handle, param)

	// OK，转换状态码
	return convertStatusCode(int(res))
}

// SetRecogMaxThreadNum 配置识别模型最大线程数
//
// @params num 最大线程数数量
func (p *Control) SetRecogMaxThreadNum(num int) error {
	// 赋值扩展参数
	var param C.RecogExpandParam
	param.key = C.INFER_THREAD
	param.val = unsafe.Pointer(uintptr(num))

	// 执行扩展参数配置
	return p.setExpandParam(&param)
}

// GetFeatLength 获取特征长度
//
// 注意：仅允许在模型初始化成功后调用
func (p *Control) GetFeatLength() (int32, error) {
	// 赋值扩展参数
	var param C.RecogExpandParam
	param.key = C.FEAT_NUM
	// 执行扩展参数获取
	err := p.getExpandParam(&param)
	if err != nil {
		return 0, err
	}
	// 转换类型
	featLen := uintptr(param.val)
	// 返回长度
	return int32(featLen), nil
}

// GetIsInherit 获取是否继承
//
//	注意：仅允许在模型初始化成功后调用
func (p *Control) GetIsInherit() (bool, error) {
	// 赋值扩展参数
	var param C.RecogExpandParam
	param.key = C.IS_INHERIT
	// 执行扩展参数获取
	err := p.getExpandParam(&param)
	if err != nil {
		return false, err
	}
	// 转换类型
	isInherit := uintptr(param.val)
	// 返回是否继承
	switch isInherit {
	case 0: // 同场景升级不可以继承
		return false, nil
	case 1: // 同场景升级可以继承
		return true, nil
	case 2: // 不同场景切换
		return true, nil
	default: // 默认可以继承
		return true, nil
	}
}
